﻿using Edu.Common.Extensions;
using Edu.Common.Lambdas.Dynamics;
using Edu.Common.Logs;
using Edu.Common.Win32;
using NetOffice.PowerPointApi;
using NetOffice.PowerPointApi.Enums;
using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using static Edu.Common.Win32.WindowEnumerator;
using PPTApplication = NetOffice.PowerPointApi.Application;
using WindowEnumerator = Edu.Common.Win32.WindowEnumerator;

namespace MainApp.Utils
{
    class PPTTools
    {
        #region 字段定义
        private static readonly Lazy<ILogger> _lazyLogger;
        protected static ILogger Logger { get => _lazyLogger?.Value; }
        #endregion

        #region 构造函数
        static PPTTools()
        {
            _lazyLogger = new Lazy<ILogger>(() => LogManager.GetLogger<PPTTools>(), System.Threading.LazyThreadSafetyMode.ExecutionAndPublication);
        }
        #endregion

        #region 公共方法
        /// <summary>
        /// 启动ppt文件
        /// </summary>
        /// <param name="pptFile"></param>
        public static void RunPPTFile(string pptFile)
        {
            if (!pptFile.FileExists()) return;
            if (CheckFileIsRun(pptFile)) return;
            //Tools.RunFile(pptFile);
            RunPPTFileByCom(pptFile);
        }
        /// <summary>
        /// 启动ppt文件(COM方式打开文件)
        /// </summary>
        /// <param name="pptFile"></param>
        public static void RunPPTFileByCom(string pptFile)
        {
            try
            {
                var application = GetApplication();
                if (application == null)
                {
                    try { application = new PPTApplication(true) { Visible = NetOffice.OfficeApi.Enums.MsoTriState.msoTrue }; }
                    catch (COMException) { }
                }
                if (application == null)
                {
                    var exec = Tools.RunFile(pptFile);
                    if (!exec) return;
                    var dateStart = DateTime.Now;
                    var dateNow = DateTime.Now;
                    while (dateNow.Subtract(dateStart).TotalMilliseconds < 20000)
                    {
                        if (CheckFileIsRun(pptFile)) break;
                        Thread.Sleep(800);
                        dateNow = DateTime.Now;
                    }
                    return;
                }
                application.DisplayAlerts = PpAlertLevel.ppAlertsNone;
                var pres = application.Presentations.Open(pptFile, NetOffice.OfficeApi.Enums.MsoTriState.msoTrue);
                if (pres == null) return;
                TryPlaySlide(pres);
            }
            catch (COMException) { }
            catch (Exception exception) { Logger?.Error("", exception); }
        }
        /// <summary>
        /// 尝试前置ppt演示窗口
        /// </summary>
        /// <param name="pptFile"></param>
        public static void TryForegroundSlideViewWindow(string pptFile)
        {
            if (!pptFile.FileExists()) return;
            var presentation = GetFilePresentation(pptFile);
            if (presentation == null) return;
            TryForegroundSlideViewWindow(presentation);
        }
        /// <summary>
        /// 返回第一页
        /// </summary>
        /// <returns></returns>
        public static bool TryGotoFirstPage()
        {
            try
            {
                var application = GetApplication();
                if (application == null) return false;
                var slideShowWindows = application.SlideShowWindows;
                if (slideShowWindows == null || slideShowWindows.Count == 0)
                    return false;
                var slideShowWindow = slideShowWindows[1];
                slideShowWindow.View.First();
                return true;
            }
            catch (COMException) { }
            catch (Exception exception) { Logger?.Error("", exception); }
            return false;
        }
        /// <summary>
        /// 尝试关闭除指定的ppt文档之外的其他ppt文档
        /// </summary>
        /// <param name="pptFile"></param>
        public static void TryCloseOtherFiles(string pptFile)
        {
            if (!pptFile.FileExists()) return;
            var application = GetApplication();
            if (application == null) return;
            try
            {
                var presentations = application.Presentations;
                if (presentations == null || presentations.Count == 0) return;
                for (var index = presentations.Count; index >= 1; index--)
                {
                    try
                    {
                        var pres = presentations[index];
                        if (pres.FullName.EqualsPath(pptFile))
                            continue;
                        pres.Saved = NetOffice.OfficeApi.Enums.MsoTriState.msoTrue;
                        pres.Close();
                    }
                    catch (Exception ex) { }
                }
            }
            catch { }
        }
        /// <summary>
        /// 获取文档窗口句柄
        /// </summary>
        /// <param name="pptFile"></param>
        /// <returns></returns>
        public static IntPtr GetDocumentHwnd(string pptFile)
        {
            if (!pptFile.FileExists()) return IntPtr.Zero;
            var pres = GetFilePresentation(pptFile);
            if (pres == null) return IntPtr.Zero;
            return GetDocumentHwnd(pres);
        }
        /// <summary>
        /// 获取特定ppt文档对应的演示窗口句柄
        /// </summary>
        /// <param name="pptFile"></param>
        /// <returns></returns>
        public static IntPtr GetSlideViewWindowHandle(string pptFile)
        {
            if (!pptFile.FileExists()) return IntPtr.Zero;
            var pres = GetFilePresentation(pptFile);
            if (pres == null) return IntPtr.Zero;
            return GetViewerHwnd(pres);
        }
        /// <summary>
        /// 切换到下一页
        /// </summary>
        public static void SlideToNext()
        {
            try
            {
                var application = GetApplication();
                if (application == null) return;
                var slideShowWindows = application.SlideShowWindows;
                if (slideShowWindows == null || slideShowWindows.Count == 0)
                    return;
                var slideShowWindow = slideShowWindows[1];
                slideShowWindow.View.Next();
            }
            catch (COMException) { }
            catch (Exception exception) { Logger?.Error("", exception); }
        }
        /// <summary>
        /// 结束ppt放映
        /// </summary>
        public static void EndSlideView()
        {
            try
            {
                var application = GetApplication();
                if (application == null) return;
                var slideShowWindows = application.SlideShowWindows;
                if (slideShowWindows == null || slideShowWindows.Count == 0)
                    return;
                for (var index = slideShowWindows.Count; index >= 1; index--)
                {
                    try
                    {
                        var slideShowWindow = slideShowWindows[index];
                        slideShowWindow.View.Exit();
                    }
                    catch (Exception ex) { }
                }
            }
            catch (COMException) { }
            catch (Exception exception) { Logger?.Error("", exception); }
        }
        /// <summary>
        /// 关闭ppt程序
        /// </summary>
        public static void ClosePptApplication()
        {
            PPTApplication application = null;
            try
            {
                application = GetApplication();
                if (application == null) return;
                var presentations = application.Presentations;
                if (presentations == null || presentations.Count == 0)
                    return;
                //关闭所有文档
                for (var index = presentations.Count; index >= 1; index--)
                {
                    try
                    {
                        //结束可能存在的演示
                        var presentation = presentations[index];
                        SlideShowWindow slideShowWindow = null;
                        try { slideShowWindow = presentation.SlideShowWindow; } catch { }
                        if (slideShowWindow != null)
                        {
                            try { slideShowWindow.View.Exit(); } catch { }
                        }
                        if (presentation.Saved == NetOffice.OfficeApi.Enums.MsoTriState.msoFalse)
                        {
                            presentation.Saved = NetOffice.OfficeApi.Enums.MsoTriState.msoTrue;
                        }
                        //退出文档
                        presentation.Close();
                    }
                    catch (Exception ex) { }
                }
            }
            catch (COMException) { }
            catch (Exception exception) { Logger?.Error("", exception); }
            finally
            {
                try { application?.Quit(); } catch { }
                KillProcess("POWERPNT"); KillProcess("wpp");
            }
        }
        #endregion

        #region 私有方法
        /// <summary>
        /// 获取当前运行的ppt程序实例
        /// </summary>
        /// <returns></returns>
        private static PPTApplication GetApplication()
        {
            try
            {

                var application = PPTApplication.GetActiveInstance();
                if (application == null)
                    return null;
                return application;
            }
            catch { }
            return null;
        }
        /// <summary>
        /// 获取指定文档的演示窗口句柄
        /// </summary>
        /// <param name="presentation"></param>
        /// <returns></returns>
        private static IntPtr GetViewerHwnd(Presentation presentation)
        {
            if (presentation == null) return IntPtr.Zero;
            try
            {
                var documentName = presentation.Name;
                return GetViewerHwnd(documentName);
            }
            catch { }
            return IntPtr.Zero;
        }
        /// <summary>
        /// 获取指定文档名称的演示窗口句柄
        /// </summary>
        /// <param name="documentName"></param>
        /// <returns></returns>
        private static IntPtr GetViewerHwnd(string documentName)
        {
            IntPtr viewerHwnd = IntPtr.Zero;
            if (documentName.IsNullOrWhiteSpace())
                return viewerHwnd;
            try
            {
                //根据窗口的类名称查找演示窗口句柄
                var enumeratorByClass = new WindowEnumerator(string.Empty, "screenClass", FilterTypes.ClassName);
                IntPtr[] handles = enumeratorByClass.EnumerateWindows(2000);
                if (null != handles && handles.Length > 0)
                {
                    foreach (var hwnd in handles)
                    {
                        var caption = ClrMethods.GetWindowText(hwnd);
                        if (caption.IndexOf(documentName) > -1 && caption.IndexOf("幻灯片放映") > -1)
                        {
                            viewerHwnd = hwnd;
                            break;
                        }
                    }
                }
                //根据窗口名称查找演示窗口句柄
                if (viewerHwnd == IntPtr.Zero)
                {
                    var enumeratorByCaption = new WindowEnumerator(new string[] { "幻灯片放映", documentName }, FilterTypes.Caption);
                    handles = enumeratorByCaption.EnumerateWindows(2000);
                    viewerHwnd = handles?.FirstOrDefault() ?? IntPtr.Zero;
                }
            }
            catch { }
            return viewerHwnd;
        }
        /// <summary>
        /// 检查ppt文件是否已经运行，如果已经运行没有处于演示状态则启动演示
        /// </summary>
        /// <param name="pptFile"></param>
        /// <returns></returns>
        private static bool CheckFileIsRun(string pptFile)
        {
            try
            {
                var presentation = GetFilePresentation(pptFile);
                if (presentation == null || presentation.Slides.Count == 0) return false;
                TryPlaySlide(presentation);
                return true;
            }
            catch { }
            return false;
        }
        /// <summary>
        /// 启动幻灯片放映
        /// </summary>
        /// <param name="presentation"></param>
        private static void TryPlaySlide(Presentation presentation)
        {
            if (presentation == null) return;
            try
            {
                var slideShowWindow = presentation.SlideShowWindow;
                if (slideShowWindow != null)
                {
                    //前置演示窗口
                    TryForegroundSlideViewWindow(presentation);
                    return;
                }
            }
            catch { }
            try
            {
                //如果窗口处于未显示状态则设置为显示状态(wps)
                try
                {
                    var application = presentation.Application;
                    if (application.Visible == NetOffice.OfficeApi.Enums.MsoTriState.msoFalse)
                    {
                        application.Visible = NetOffice.OfficeApi.Enums.MsoTriState.msoTrue;
                    }
                }
                catch { }
                //启动ppt演示
                var settings = presentation.SlideShowSettings;
                settings.RangeType = PpSlideShowRangeType.ppShowAll;
                settings.LoopUntilStopped = NetOffice.OfficeApi.Enums.MsoTriState.msoTrue;
                settings.Run();
                //前置演示窗口
                TryForegroundSlideViewWindow(presentation);
                //设置为已保存状态
                presentation.Saved = NetOffice.OfficeApi.Enums.MsoTriState.msoTrue;
            }
            catch { }
        }
        /// <summary>
        /// 尝试前置ppt演示窗口
        /// </summary>
        /// <param name="pptFile"></param>
        private static void TryForegroundSlideViewWindow(Presentation presentation)
        {
            if (presentation == null) return;
            //检查是否处于演示状态
            SlideShowWindow slideShowWindow = null;
            try { slideShowWindow = presentation.SlideShowWindow; }
            catch { }
            if (slideShowWindow == null) return;
            //激活窗口
            try { slideShowWindow.Activate(); }
            catch { }
            //前置窗口
            var hwnd = GetViewerHwnd(presentation);
            if (hwnd == IntPtr.Zero) return;
            try { Tools.ForegroundWindow(hwnd); } catch { }
        }
        /// <summary>
        /// 获取指定文件对应的<see cref="Presentation"/>实例
        /// </summary>
        /// <param name="pptFile"></param>
        /// <returns></returns>
        private static Presentation GetFilePresentation(string pptFile)
        {
            Presentation presentation = null;
            var application = GetApplication();
            if (application == null) return presentation;
            try
            {
                var presentations = application.Presentations;
                if (presentations == null || presentations.Count == 0) return presentation;
                for (var index = presentations.Count; index >= 1; index--)
                {
                    try
                    {
                        var pres = presentations[index];
                        if (pres.FullName.EqualsPath(pptFile))
                        {
                            presentation = pres;
                            break;
                        }
                    }
                    catch (Exception ex) { }
                }
            }
            catch { }
            return presentation;
        }
        /// <summary>
        /// 获取文档窗口句柄
        /// </summary>
        /// <param name="presentation"></param>
        /// <returns></returns>
        private static DocumentWindow GetDocumentWindow(Presentation presentation)
        {
            if (presentation == null)
                return null;
            //获取文档窗口集合
            DocumentWindows windows;
            try { windows = presentation.Windows; }
            catch { return null; }
            //检查窗口集合
            if (windows == null)
                return null;
            var wCount = 0;
            try { wCount = windows.Count; } catch { }
            for (var index = wCount; index >= 1; index--)
            {
                DocumentWindow window = null;
                try { window = windows[index]; } catch { }
                if (window == null)
                    continue;
                Presentation tmpPresentation = null;
                try { tmpPresentation = window.Presentation; }
                catch { }
                //检查窗口的文档与当前文档是否一致
                if (tmpPresentation != presentation)
                    continue;
                return window;
            }
            return null;
        }
        /// <summary>
        /// 获取文档窗口句柄
        /// </summary>
        /// <param name="presentation"></param>
        /// <returns></returns>
        private static IntPtr GetDocumentHwnd(Presentation presentation)
        {
            if (presentation == null) return IntPtr.Zero;
            //获取文档窗口
            var documentWindow = GetDocumentWindow(presentation);
            if (documentWindow == null)
                return IntPtr.Zero;
            int handle = 0;
            //尝试获取文档窗口句柄属性
            try { handle = documentWindow?.HWND ?? 0; } catch { }
            if (handle > 0) return new IntPtr(handle);
            //通过枚举桌面窗口获取文档窗口句柄
            try { handle = PptWindowUtils.GetHwnd(documentWindow, out HwndSourceTypes _, ignoreCache: true); } catch { }
            if (handle > 0) return new IntPtr(handle);
            return IntPtr.Zero;
        }
        /// <summary>
        /// 结束指定名称的进程
        /// </summary>
        /// <param name="name"></param>
        private static void KillProcess(string name)
        {
            if (name.IsNullOrWhiteSpace()) return;
            var processes = Process.GetProcessesByName(name);
            if ((processes?.Length ?? 0) == 0) return;
            foreach (var proc in processes)
            {
                try { proc?.Kill(); } catch { }
            }
        }
        #endregion
    }
}
